<?php

namespace app\commands;

use app\models\ActiveMqApi;
use app\models\Card;
use app\models\Isp;
use Yii;
use yii\console\Controller;

/**
 * 队列处理
 * Class QueueController
 * @package app\commands
 */
class QueueController extends Controller
{
    const CHILD_PROC_AMOUNT = 8; // 最大进程数
    static $currentProcAmount = 0; // 当前进程数

    /**
     * 队列处理
     * 队列路径：/queue/card
     * 消息定义：
     * {
     *     action: 'card/refresh', // 命令
     *     // 其它参数
     *     iccid: '####'
     * }
     * @throws \yii\db\Exception
     */
    public function actionExec()
    {
        $this->stdout(date('Y-m-d H:i:s') . chr(9) . 'StartQueueExec' . PHP_EOL);
        declare(ticks = 1);
        pcntl_signal(SIGCHLD, function ($sig) {
            switch ($sig) {
                case SIGCHLD:
                    static::$currentProcAmount--;
                    break;
            }
        });

        while (true) {
            static::$currentProcAmount++;
            $pid = pcntl_fork();
            if ($pid) { // 父进程
                if (static::$currentProcAmount >= static::CHILD_PROC_AMOUNT) {
                    pcntl_wait($status);
                }
            } else { // 子进程
                $mq = (new ActiveMqApi('/queue/card'));
                $mq->subscribe();
                while (true) {
                    if (!$mq->hasFrame()) {
                        Yii::$app->db->createCommand('SELECT NOW()')->query();
                        continue;
                    }
                    $frame = $mq->readFrame();
                    if (!$frame) {
                        continue;
                    }
                    // 队列消息处理 开始
                    $trans = time();
                    $mq->begin($trans);
                    $data = $frame->body;
                    Yii::info($data, __METHOD__);
                    $json = json_decode($data, true);
                    try {
                        if (!isset($json['action'])) {
                            $json['action'] = '';
                        }
                        // 业务逻辑 开始
                        switch ($json['action']) {
                            case 'card/refresh':
                                $this->cardRefresh($json['iccid']);
                                break;
                            case 'card/refresh-batch':
                                $this->cardRefreshBatch($json['isp_id'], $json['iccid_list']);
                                break;
                            case 'isp/sync-card':
                                $this->ispSyncCard($json['isp_id']);
                                break;
                            case 'card/refresh-daily-usage':
                                $this->cardRefreshDailyUsage($json['date'], $json['card_id']);
                                break;
                            default:
                                $this->stdout(date('Y-m-d H:i:s') . chr(9) . 'UNKNOWN_QUEUE_COMMAND' . chr(9) . $data . PHP_EOL);
                        }
                        // 业务逻辑 结束
                    } catch (\Exception $e) {
                        if ($e->getCode() == 400101) {
                            sleep(1);
                            $mq->send($json);
                        } else {
                            Yii::error($e->getMessage());
                            Yii::error($e->getTraceAsString());
                            $this->stderr($e->getMessage() . PHP_EOL);
                            $this->stderr($e->getTraceAsString());
                        }
                    }
                    $mq->ack($frame);
                    $mq->commit($trans);
                    // 队列消息处理 结束
                }
                exit;
            }
        }
    }

    /**
     * 刷新卡信息
     * @param string $iccid ICCID
     * @throws \Exception
     */
    private function cardRefresh($iccid)
    {
        $this->stdout(date('Y-m-d H:i:s') . chr(9) . 'card/refresh' . chr(9) . $iccid . PHP_EOL);
        $card = Card::findOne(['iccid' => $iccid]);
        $card->refreshBasic();
    }

    /**
     * 批量刷新卡信息
     * @param integer $ispId 接口编号
     * @param array $iccidList ICCID列表
     * @throws \Exception
     */
    private function cardRefreshBatch($ispId, $iccidList)
    {
        $this->stdout(date('Y-m-d H:i:s') . chr(9) . 'card/refresh-batch' . chr(9) . $ispId . chr(9) . count($iccidList) . PHP_EOL);
        Card::batchRefreshBasic($ispId, $iccidList);
    }

    /**
     * 同步Jasper运营商卡信息
     * @param $ispId
     * @throws \Exception
     */
    private function ispSyncCard($ispId)
    {
        $this->stdout(date('Y-m-d H:i:s') . chr(9) . 'isp/sync-card' . chr(9) . $ispId . PHP_EOL);
        $isp = Isp::findOne(['id' => $ispId]);
        $api = new \app\models\jasper\Api($isp->data);
        $newBatchNo = Card::generateBatchNo();
        $pageNumber = 1;
        while (true) {
            $this->stdout(date('Y-m-d H:i:s') . chr(9) . 'Jasper/GetModifiedTerminals' . chr(9) . $pageNumber . PHP_EOL);
            $resp = $api->GetModifiedTerminals(0, null, $pageNumber);
            $iccids = $resp->iccids->iccid;
            foreach ($iccids as $iccid) {
                $card = Card::findOne(['iccid' => $iccid]);
                if (empty($card)) {
                    $card = new Card();
                    $card->api_id = $isp->api_id;
                    $card->isp_id = $isp->id;
                    $card->iccid = $iccid;
                    $card->status = Card::STATUS_TEST_READY;
                    $card->batch_no = $newBatchNo;
                    $card->create_time = time();
                    $card->remark = '通过接口同步创建';
                    if (!$card->save()) {
                        $this->stderr(print_r($card->errors, true));
                    }
                }
            }
            try {
                Card::batchRefreshBasic($isp->id, $iccids);
            } catch (\Exception $e) {
                Yii::error($e->getMessage());
                Yii::error($e->getTraceAsString());
            }
            $totalPages = $resp->totalPages;
            $pageNumber++;
            if ($pageNumber > $totalPages) {
                break;
            }
        }
    }

    /**
     * 刷新卡日用量
     * @param string $date 日期Ymd
     * @param integer $cardId 卡编号
     * @return boolean
     */
    private function cardRefreshDailyUsage($date, $cardId)
    {
        $this->stdout(date('Y-m-d H:i:s') . chr(9) . 'card/refresh-daily-usage' . chr(9) . $date . ':' . $cardId . PHP_EOL);
        $card = Card::findOne(['id' => $cardId]);
        if (empty($card)) {
            return false;
        }
        return $card->refreshDailyUsage($date);
    }
}
